import {Geometry} from '../core/Geometry.js'
import {BufferGeometry} from '../core/BufferGeometry.js'
import {Float32BufferAttribute} from '../core/BufferAttribute.js'
import {ShapeUtils} from '../extras/ShapeUtils.js'

// ShapeGeometry

class ShapeGeometry extends Geometry {
  constructor(shapes, curveSegments) {
    super()
    this.type = 'ShapeGeometry'

    if (typeof curveSegments === 'object') {
      console.warn('THREE.ShapeGeometry: Options parameter has been removed.')

      curveSegments = curveSegments.curveSegments
    }

    this.parameters = {
      shapes: shapes,
      curveSegments: curveSegments,
    }

    this.fromBufferGeometry(new ShapeBufferGeometry(shapes, curveSegments))
    this.mergeVertices()
  }

  toJSON() {
    const data = Geometry.prototype.toJSON.call(this)

    const shapes = this.parameters.shapes

    return toJSON(shapes, data)
  }
}

// ShapeBufferGeometry

class ShapeBufferGeometry extends BufferGeometry {
  constructor(shapes, curveSegments) {
    super()
    this.type = 'ShapeBufferGeometry'

    this.parameters = {
      shapes: shapes,
      curveSegments: curveSegments,
    }

    curveSegments = curveSegments || 12

    // buffers

    const indices = []
    const vertices = []
    const normals = []
    const uvs = []

    // helper variables

    let groupStart = 0
    let groupCount = 0

    // allow single and array values for "shapes" parameter

    if (Array.isArray(shapes) === false) {
      addShape(shapes)
    } else {
      for (let i = 0; i < shapes.length; i++) {
        addShape(shapes[i])

        this.addGroup(groupStart, groupCount, i) // enables MultiMaterial support

        groupStart += groupCount
        groupCount = 0
      }
    }

    // build geometry

    this.setIndex(indices)
    this.setAttribute('position', new Float32BufferAttribute(vertices, 3))
    this.setAttribute('normal', new Float32BufferAttribute(normals, 3))
    this.setAttribute('uv', new Float32BufferAttribute(uvs, 2))

    // helper functions

    function addShape(shape) {
      const indexOffset = vertices.length / 3
      const points = shape.extractPoints(curveSegments)

      let shapeVertices = points.shape
      const shapeHoles = points.holes

      // check direction of vertices

      if (ShapeUtils.isClockWise(shapeVertices) === false) {
        shapeVertices = shapeVertices.reverse()
      }

      for (let i = 0, l = shapeHoles.length; i < l; i++) {
        const shapeHole = shapeHoles[i]

        if (ShapeUtils.isClockWise(shapeHole) === true) {
          shapeHoles[i] = shapeHole.reverse()
        }
      }

      const faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles)

      // join vertices of inner and outer paths to a single array

      for (let i = 0, l = shapeHoles.length; i < l; i++) {
        const shapeHole = shapeHoles[i]
        shapeVertices = shapeVertices.concat(shapeHole)
      }

      // vertices, normals, uvs

      for (let i = 0, l = shapeVertices.length; i < l; i++) {
        const vertex = shapeVertices[i]

        vertices.push(vertex.x, vertex.y, 0)
        normals.push(0, 0, 1)
        uvs.push(vertex.x, vertex.y) // world uvs
      }

      // incides

      for (let i = 0, l = faces.length; i < l; i++) {
        const face = faces[i]

        const a = face[0] + indexOffset
        const b = face[1] + indexOffset
        const c = face[2] + indexOffset

        indices.push(a, b, c)
        groupCount += 3
      }
    }
  }

  toJSON() {
    const data = BufferGeometry.prototype.toJSON.call(this)

    const shapes = this.parameters.shapes

    return toJSON(shapes, data)
  }
}

//

function toJSON(shapes, data) {
  data.shapes = []

  if (Array.isArray(shapes)) {
    for (let i = 0, l = shapes.length; i < l; i++) {
      const shape = shapes[i]

      data.shapes.push(shape.uuid)
    }
  } else {
    data.shapes.push(shapes.uuid)
  }

  return data
}

export {ShapeGeometry, ShapeBufferGeometry}
